(function () {
    "use strict";
    var realXHR = window.XMLHttpRequest;

    function hookAjax(options) {

        function XMLHttpRequest() {
            var xhr = new realXHR();
            this.xhr = xhr;

            //事件相关属性
            var eventHandlers = [];
            //方法
            var methods = [];
            //其他属性
            var props = [];
            //其他属性中的数据属性
            var dataProps = [];
            //访问器属性中不可修改的
            var accessPropsCantSet = [];
            //访问器属性中可以修改的
            var accessPropsCanSet = [];

            for (var prop in xhr) {
                if (typeof xhr[prop] === "function") {
                    methods.push(prop);
                } else {
                    if (prop.startsWith("on")) {
                        eventHandlers.push(prop);
                    } else {
                        props.push(prop);
                    }
                }
                //将原生xhr的属性挂载。 以提供对原生xhr对象的使用。
                this["_" + prop] = xhr[prop];
            }

            props.forEach((prop) => {
                var desc = getDescIterable(realXHR.prototype, prop);
                if (desc.get || desc.set) {
                    if (desc.set) {
                        accessPropsCanSet.push(prop);
                    } else {
                        accessPropsCantSet.push(prop)
                    }
                } else {
                    dataProps.push(prop);
                }
            });

            // console.log(`methods:${methods}`);
            // console.log(`eventHandlers:${eventHandlers}`);
            // console.log(`数据属性:${dataProps}`);
            // console.log(`访问器可改属性:${accessPropsCanSet}`);
            // console.log(`访问器不可改属性:${accessPropsCantSet}`);

            methods.forEach((method) => {
                this[method] = generateMethodHook(this, method, options);
            })

            eventHandlers.forEach((eventHandler) => {
                Object.defineProperty(this, eventHandler, {
                    configurable: true,
                    enumerable: true,
                    set: generateEventHandlerSetter(this, eventHandler, options),
                    get: generateEventHandlerGetter(this, eventHandler, options)
                })
            });

            //数据属性暂不提供拦截，因为没啥用。原生XHR的数据属性全是一些不可修改的标志位 UNSENT,OPENED,HEADERS_RECEIVED,LOADING,DONE。

            //这里只提供对访问器属性的拦截


            accessPropsCanSet.forEach((prop) => {
                Object.defineProperty(this, prop, {
                    configurable: true,
                    enumerable: true,
                    set: generateAccessCanSetPropSetter(this, prop, options),
                    get: generateAccessCanSetPropGetter(this, prop, options)
                })
            })

            accessPropsCantSet.forEach((prop) => {
                var generatedMethod = generateAccessCantSetPropSetterAndGetter(this, prop, options);
                Object.defineProperty(this, prop, {
                    configurable: true,
                    enumerable: true,
                    set: generatedMethod.setter,
                    get: generatedMethod.getter
                })
            })
        }

        window.XMLHttpRequest = XMLHttpRequest;
    }


    /**
     * 生成普通方法的拦截
     * @param {*} thisArg 
     * @param {*} method 
     * @param {*} options 
     */
    function generateMethodHook(thisArg, method, options) {
        var realXHRObj = thisArg.xhr;
        return function () {
            var args = [].slice.call(arguments);
            /**
             *   这里需要注意的是, 拦截函数的this取值是hook的XHR对象，参数是函数参数和原生xhr对象。
             */
            if (options[method] && options[method].call(thisArg, args, realXHRObj)) {
                return;
            }
            return realXHRObj[method].apply(realXHRObj, args)
        }
    }

    /**
     * 生成事件的set方法拦截
     * @param {*} thisArg 
     * @param {*} eventHanlder 
     * @param {*} options 
     */
    function generateEventHandlerSetter(thisArg, eventHanlder, options) {
        var realXHRObj = thisArg.xhr;
        return function (newValue) {
            realXHRObj[eventHanlder] = function () {
                var args = [].slice.call(arguments);
                /**
                 * 这里的拦截函数的参数以及this取值均是指向hook对象。
                 */
                if (options[eventHanlder] && options[eventHanlder].call(thisArg, thisArg)) {
                    return;
                }
                return newValue.apply(thisArg.xhr, args);
            }
        }
    }

    /**
     * 生成事件的get方法拦截
     */
    function generateEventHandlerGetter(thisArg, eventHandler, options) {
        var realXHRObj = thisArg.xhr;
        return function () {
            //这里对于事件的getter不提供拦截，因为似乎没有什么实用性。
            return realXHRObj[eventHandler];
        }
    }


    /**
     * 生成可修改的访问器属性的set拦截
     * @param {*} thisArg 
     * @param {*} prop 
     * @param {*} options 
     */
    function generateAccessCanSetPropSetter(thisArg, prop, options) {
        var realXHRObj = thisArg.xhr;
        return function (newValue) {
            if (options[prop] && options[prop].setter) {
                newValue = options[prop].setter.call(thisArg, newValue, thisArg);
            }
            realXHRObj[prop] = newValue;
        }
    }

    /**
     * 生成可修改的访问器属性的get拦截
     * @param {*} thisArg 
     * @param {*} prop 
     * @param {*} options 
     */
    function generateAccessCanSetPropGetter(thisArg, prop, options) {
        var realXHRObj = thisArg.xhr;
        return function () {
            var ret = realXHRObj[prop];
            if (options[prop] && options[prop].getter) {
                return options[prop].getter.call(thisArg, ret, thisArg);
            }
            return ret;
        }
    }


    /**
     * 生成不可修改的访问器属性的get拦截和set拦截。
     * @param {*} thisArg 
     * @param {*} prop 
     * @param {*} options 
     */
    function generateAccessCantSetPropSetterAndGetter(thisArg, prop, options) {

        var realXHRObj = thisArg.xhr;
        var inited = false;

        var initMethod = function () {
            if (!inited) {
                thisArg["__" + prop] = realXHRObj[prop];
                inited = true;
            }
        }

        var getter = function () {
            initMethod();
            var ret = thisArg["__" + prop];
            if (options[prop] && options[prop].getter) {
                return options[prop].getter.call(thisArg, ret, thisArg);
            }
            return ret;
        }

        var setter = function (newValue) {
            initMethod();
            if (options[prop] && options[prop].setter) {
                newValue = options[prop].setter.call(thisArg, newValue, thisArg);
            }
            thisArg["__" + prop] = newValue;
        }

        return {
            getter: getter,
            setter: setter
        }
    }

    /**
     * 在对象及其原型链上搜索某个属性的描述属性。 
     * @param {*} obj 
     * @param {*} prop 
     */
    function getDescIterable(obj, prop) {
        var desc;
        while (!(desc = Object.getOwnPropertyDescriptor(obj, prop))) {
            if (obj != null) {
                obj = obj.__proto__;
            } else {
                break;
            }
        }
        return desc;
    }

    window.hookAjax = hookAjax;

}());